home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1993…ch: Other People's Memory / ADC Developer CD (1993-03) (''Other People's Memory'')_iso / Dev.CD Mar 93.iso / Technical Documentation / Sample Code / DTS.Lib & Samples / =Zippo.Tutorial < prev   
Encoding:
Text File  |  1992-10-22  |  19.6 KB  |  490 lines  |  [TEXT/MPS ]

  1. Zippo QuickStart:
  2.  
  3. Step 1 is to build the libraries.  If you have not built the libraries, please see either the
  4. “==MPW read me” or “==THINK read me” file.
  5.  
  6. Getting started with the tutorial:
  7.  
  8. Step 1)  Copy the Zippo project folder.
  9. Step 2)  If you are using MPW, you may wish to delete the files “Zippo.π” and “Zippo.π.rsrc”.
  10.          If you are using THINK, you may wish to delete the file “Zippo.make”.
  11. Step 3)  You may wish to change the name of the project from Zippo.  Change the file name
  12.          for those files names Zippo-something.  This tutorial will assume that you have
  13.          left the project names Zippo.
  14. Step 4)  Build Zippo.  If you changed the project name, and you are using MPW, you will have
  15.          to change the “AppName” in the make file.
  16. Step 5)  Run Zippo.  Note that the “File” and “Edit” menus are already implemented.
  17.          (The rest of the menus are your problem.)
  18.  
  19. So, you now have the standard Zippo running.  Let's change it.
  20.  
  21. The most noticeable change you can make is to get something to draw in the window.  The code
  22. for drawing in the window is in the file “Window2.c”.  The “2” in the name suggests that there
  23. is another file containing window source code.  This is the case.  There is a file called
  24. “Window.c” in the library.  What this suggests is that windowing responsibilities are split
  25. between the library and the application, which is also the case.
  26.  
  27. You will note that you have another file in Zippo named this way.  Filing responsibilities
  28. are also split between the application and the library, and hence you have a source file
  29. called “File2.c”.
  30.  
  31. So, let's open the source file “Window2.c” and take a look.  A quick way to see what this
  32. source file is about is to search for •• marks.  The functions that have •• in the comments
  33. are automatically called by the library's framework code.  All you need to do is to drop
  34. some code into these functions, and the framework calls it at the right times.
  35.  
  36. Sounds good.  Does it work?  Let's try it.
  37.  
  38. Find the function “ImageDocument” and add some code to it so it looks like this:
  39.  
  40. OSErr    ImageDocument(FileRecHndl frHndl)
  41. {
  42. #pragma unused (frHndl)
  43.  
  44.     Rect    rct;
  45.  
  46.     SetRect(&rct, 10, 10, 300, 200);
  47.     FrameRect(&rct);
  48.     return(noErr);
  49. }
  50.  
  51.  
  52. Build it and run it.  A simple rect shows up in the window.  We can note some interesting
  53. behaviors at this point.  Note that the zoom box zooms to the size that the window already
  54. is.  That is because the window is zoomed to the document size.  The default document size
  55. is the size of the window based on the 'WIND' resource for the window (number 128).
  56.  
  57. We can change this in two ways:
  58.  
  59. 1) We can change the resource 'WIND', ID#128.
  60. 2) We can change the code to override the default document size.
  61.  
  62. Method 1 is too simple, so let's do method 2.
  63.  
  64. At the point in the document where ImageDocument is called, it is too late to change the
  65. size of the window.  However, we could change the document size at this point.  Even
  66. though ImageDocument isn't the place to do this, let's change it there anyway.
  67.  
  68. There is a library call to change the document size.  It records the new size, plus it
  69. adjusts the scrollbar max values to reflect that more area can now be scrolled to.
  70. Here's the prototype for the call:
  71.  
  72. void    SetDocSize(FileRecHndl frHndl, long hSize, long vSize);
  73.  
  74. Just pass in the file record handle (frHndl), and the new horizontal and vertical size
  75. of the document.
  76.  
  77. ImageDocument is passed in the frHndl of the document to be imaged, so all we have to do
  78. is to pass it to SetDocSize.
  79.  
  80. NOTE:  For MPW users, passing frHndl to SetDocSize means that frHndl is no longer not used.
  81.        You probably want to remove the #pragma unused when you add this code.
  82.  
  83.  
  84. So now ImageDocument might look like this:
  85.  
  86. OSErr    ImageDocument(FileRecHndl frHndl)
  87. {
  88.     Rect    rct;
  89.  
  90.     SetDocSize(frHndl, 2000, 2000);        /* Make document plenty big. */
  91.     SetRect(&rct, 10, 10, 300, 200);
  92.     FrameRect(&rct);
  93.     return(noErr);
  94. }
  95.  
  96. Now the zoom actually changes the size of the window.  However, it becomes apparent that
  97. there is something wrong when the window is zoomed, compared to how the window initially
  98. comes up.  When the window initially comes up, the scrollbars are still inactive, but
  99. when the window is zoomed, they become active.  They should be active all of the time, since
  100. we set the document size substantially greater than the viewable part of the window.
  101.  
  102. So, what's the deal?
  103.  
  104. The deal is that the scrollbars are active when the window initially comes up.  They just
  105. don't look it.  Try creating a new window and then start scrolling right away.  The scrollbars
  106. do actually work.  Well, this is a bug, isn't it?
  107.  
  108. Yep.  It's a bug.  The bug is that SetDocSize didn't redisplay the scrollbars when we called
  109. it.  Actually SetDocSize is fine.  The problem is that the scrollbars are clipped out of the
  110. drawing area when we are in ImageDocument.  This is actually good, as we don't want the
  111. content of the window drawing over our scrollbars.  The library first removes the frame area
  112. from the drawable portion of a window, and then the library calls ImageDocument.
  113.  
  114. What this means is that the SetDocSize call doesn't belong in the ImageDocument function.
  115. (I said it wasn't a good place for it.)  Well, where does it belong?  You could place the
  116. call in the function InitContent.  InitContent is called after the window is created, and
  117. you now want to initialize the content of the window.  Moving the SetDocSize call to here
  118. will make this work quite nicely.  Try it.  Put the ImageDocument function back to the way
  119. it was prior to our adding the SetDocSize call to it, and place the SetDocSize call in the
  120. “Window2.c” function InitContent.  (MPW users have to contend with “#pragma unused” statements.)
  121. The InitContent function would look like this"
  122.  
  123. OSErr    InitContent(FileRecHndl frHndl, WindowPtr window)
  124. {
  125. #pragma unused (window)
  126.  
  127.     SetDocSize(frHndl, 2000, 2000);        /* Make document plenty big. */
  128.     return(noErr);
  129. }
  130.  
  131. Now the scrollbars appear initially correct.  Note that the initial window size hasn't changed.
  132. As a point of clarification, I should state that InitContent is called only once per window.
  133. It is only called when a window is getting created.  ImageDocument gets called every time the
  134. window needs redrawing (or when the user is printing, but more on this later).
  135.  
  136. What if we wanted the window to initially open to reflect the size of the document?  Of course
  137. a document as large as 2000,2000 isn't going to fit on many monitors, but how about trying to
  138. open the document this big, not to exceed the size of the monitor.  What this means is setting
  139. the document size from within the InitContent function is too late.  The window has already
  140. been created and sized, and InitContent is being called to make changes to the content of a
  141. window, not the window itself.
  142.  
  143. So, if InitContent is too late, where isn't it too late?
  144.  
  145. Here's how you work with documents and frameworks.  You first try to create a document,
  146. (an frHndl), and if you succeed, you then give the document a window.  You can do stuff
  147. between creating a document and giving the document a window.  The user doesn't see the
  148. actual document.  They just see the window you give the document, so until you give the
  149. document a window, the user will see nothing.  This is where you would want to set the
  150. document size.
  151.  
  152. Since you want to set the document size prior to having a window, it is reasonable to
  153. expect that you will have to add code to some file other than the “Window2.c” file.
  154. Again, this is the case.  You want to add code to the file “File2.c”.  The function that
  155. you want to add code to is called InitDocument.
  156.  
  157. InitDocument looks like this:
  158.  
  159.  
  160. OSErr    InitDocument(FileRecHndl frHndl)
  161. {
  162.     OSErr    err;
  163.  
  164.     err = noErr;
  165.  
  166.     switch ((*frHndl)->fileState.sfType) {
  167.         case kDocFileType:
  168.             err = DefaultInitDocument(frHndl, kVersion, kMaxNumUndos, kNumSaveUndos);
  169.             if (!err) {
  170.                 /* Any additional document initialization could go here. */
  171.             }
  172.             break;
  173. #if VH_VERSION
  174.         case kViewHierFileType:
  175.             return(VHInitDocument(frHndl));
  176.             break;
  177. #endif
  178.     }
  179.  
  180.     return(err);
  181. }
  182.  
  183. InitDocument is given a file reference (frHndl).  The frHndl contains a bunch of file
  184. information, one part of which is the file type.  The switch statement gets this type
  185. from the frHndl and then the case statements do the appropriate thing based on that type.
  186.  
  187. You don't have to worry about the kViewHierFileType case statement.  That is there so that
  188. the View Hierarchy debugging facility is available.  Discarding this file type, there is
  189. only one file type left, kDocFileType.  If you have only one type of document in your
  190. application, this is all you will ever need.  If you add document types to your application,
  191. you will end up with more case statements here.
  192.  
  193. You want to replace the comment “Any additional document...” with your code, so that the
  194. function looks like this:
  195.  
  196. OSErr    InitDocument(FileRecHndl frHndl)
  197. {
  198.     OSErr    err;
  199.  
  200.     err = noErr;
  201.  
  202.     switch ((*frHndl)->fileState.sfType) {
  203.         case kDocFileType:
  204.             err = DefaultInitDocument(frHndl, kVersion, kMaxNumUndos, kNumSaveUndos);
  205.             if (!err) {
  206.                 SetDocSize(frHndl, 2000, 2000);
  207.             }
  208.             break;
  209. #if VH_VERSION
  210.         case kViewHierFileType:
  211.             return(VHInitDocument(frHndl));
  212.             break;
  213. #endif
  214.     }
  215.  
  216.     return(err);
  217. }
  218.  
  219.  
  220. Don't forget to yank the SetDocSize call out of InitContent, as we have taken care of
  221. setting the document size elsewhere and don't need it anymore.
  222.  
  223.  
  224. So that was reasonable painless.  What else can we easily do?
  225.  
  226. How about adding a ruler to our window?
  227.  
  228. Since we are in the function InitDocument, let's set another document attribute.  Let's set
  229. the topSidebar to a non-zero value and see what happens.  Add the following lines next to the
  230. SetDocSize call:
  231.  
  232.     SetSidebarSize(frHndl, kwNoChange, 40);
  233.         /* Don't change the left sidebar (leave it 0), but set the top sidebar to 40. */
  234.  
  235. Now when you run the application, you see that the vertical scrollbar doesn't go all the way
  236. to the top anymore.  There's a 40 pixel gap at the top of the window.  Try scrolling the rect
  237. around the window.  Note that it is clipped out of this area.  This area shouldn't scroll with
  238. with document, as we want to use it as a ruler.  All we need to do now is to write some code to
  239. draw the ruler.
  240.  
  241. So, where does our ruler drawing code go?  It goes in a function called DrawFrame.  DrawFrame
  242. is found in the “Window2.c” file.  It is also called automatically by the framework when the
  243. frame needs redrawing.  I consider it part of the frame, just like the scrollbars and grow
  244. icon.  The frame portion of the window can't be drawn over by the ImageDocument procedure.
  245. This is why the rect gets scrolled behind this top area.
  246.  
  247. Here's some quick-and-ugly ruler code that we can use:
  248.  
  249. void    DrawFrame(FileRecHndl frHndl, WindowPtr window)
  250. {
  251.     short    i;
  252.  
  253.     MoveTo(0, 39);                    /* Draw a bottom line for the ruler bottom. */
  254.     Line(20000, 0);                    /* Make it plenty long. */
  255.  
  256.     for (i = 0; i < 16; ++i) {
  257.         MoveTo(72 * i, 0);
  258.         Line(0, 16);                /* Draw inch marks. */
  259.  
  260.         MoveTo(72 * i + 36, 0);        /* Draw 1/2 inch marks. */
  261.         Line(0, 8);
  262.  
  263.         MoveTo(72 * i + 18, 0);        /* Draw 1/4 inch marks. */
  264.         Line(0, 4);
  265.         MoveTo(72 * i + 54, 0);
  266.         Line(0, 4);
  267.     }
  268. }
  269.  
  270. We could add text to show the inches and stuff, but this is just a tutorial, so the heck with it.
  271.  
  272. Now, let's run the application and note a problem.  If we scroll vertically, everything works
  273. just fine, but if we scroll horizontally, we should really scroll the ruler, too.
  274.  
  275. No problem.  The framework calls us at the right time.  The function ScrollFrame is called
  276. when the document is scrolled.  Often you will do nothing, as you may not even have rulers
  277. in your document, but here we do care.
  278.  
  279. There are a couple of ways we could address this.  One is to use ScrollRect to scroll the
  280. ruler the right amount, and then call DrawFrame.  Here's some code to do this:
  281.  
  282. void    ScrollFrame(FileRecHndl frHndl, WindowPtr window, long dh, long dv)
  283. {
  284.     WindowPtr    oldPort;    
  285.     Rect        rct;
  286.     RgnHandle    rgn;
  287.  
  288.     GetPort(&oldPort);
  289.     SetPort(window);
  290.     SetOrigin(0, 0);
  291.  
  292.     rct = window->portRect;        /* Get the window's portRect, so we can scroll the top (ruler). */
  293.     rct.bottom = 39;            /* Not 40, as there is no need to scroll the bottom line. */
  294.     rgn = NewRgn();
  295.     ScrollRect(&rct, dh, 0, rgn);        /* Only scroll horizontally. */
  296.     DisposeRgn(rgn);
  297.     DrawFrame(frHndl, window);
  298.  
  299.     SetPort(oldPort);
  300. }
  301.  
  302.  
  303. The only problem is that the code we wrote for DrawFrame assumed an origin of 0,0.  Since
  304. the ruler can now scroll, we have to take this into account.  Here's the new DrawFrame
  305. code that takes the horizontal origin into account:
  306.  
  307. void    DrawFrame(FileRecHndl frHndl, WindowPtr window)
  308. {
  309.     Point    org;
  310.     short    i;
  311.  
  312.     GetContentOrigin(window, &org);
  313.     SetOrigin(org.h, 0);
  314.  
  315.     MoveTo(0, 39);
  316.     Line(20000, 0);
  317.  
  318.     for (i = 0; i < 16; ++i) {
  319.         MoveTo(72 * i, 0);
  320.         Line(0, 16);
  321.  
  322.         MoveTo(72 * i + 36, 0);
  323.         Line(0, 8);
  324.  
  325.         MoveTo(72 * i + 18, 0);
  326.         Line(0, 4);
  327.         MoveTo(72 * i + 54, 0);
  328.         Line(0, 4);
  329.     }
  330.  
  331.     SetOrigin(0, 0);
  332. }
  333.  
  334.  
  335. Note that it restores the origin to 0,0 before it leaves.  That's the rule.  The framework
  336. expects it to be 0,0, so please make it so when you leave.
  337.  
  338.  
  339. So that covers how to scroll the ruler using the toolbox call ScrollRect.  Here's a way to
  340. scroll the ruler without calling ScrollRect.  We first change ScrollFrame to simply
  341. call DrawFrame, and then we let DrawFrame do all the work.
  342.  
  343. We can use the offscreen drawing package GWLayers to first draw the ruler offscreen, and
  344. then transfer it to the window.  Here's the DrawFrame code to do this:
  345.  
  346.  
  347. void    DrawFrame(FileRecHndl frHndl, WindowPtr window)
  348. {
  349.     WindowPtr    oldPort;
  350.     Point        oldOrg, contOrg;
  351.     LayerObj    windowLayer, rulerLayer;
  352.     Rect        rct;
  353.     short        i;
  354.  
  355.     GetPort(&oldPort);                    /* The framework sets the port for us, and it sets */
  356.     SetPort(window);                    /* the origin to 0,0, but now ScrollFrame can call */
  357.     oldOrg.h = window->portRect.left;    /* here, so do the port saving and origin stuff,   */
  358.     oldOrg.v = window->portRect.top;    /* since we don't know where we were called from.  */
  359.                                         /* The upper-left of the window's portRect is the  */
  360.                                         /* origin, so by saving those, we will be able to  */
  361.                                         /* set the origin back to whatever it was before.  */
  362.  
  363.     GetContentOrigin(window, &contOrg);    /* Find out where the document is scrolled. */
  364.     SetOrigin(contOrg.h, 0);            /* Set the window's horizontal origin to that. */
  365.  
  366.     NewLayer(&windowLayer, nil, nil, window, 0, 0L);    /* We are using GWLayers to draw offscreen. */
  367.     rct        = window->portRect;                        /* The NewLayer call created a layer object */
  368.     rct.bottom = 40;                                    /* for the whole window.  We only want the  */
  369.     (*windowLayer)->dstRect = rct;                        /* top 40 pixels, so set the dstRect to     */
  370.                                                         /* just the top portion of the window.      */
  371.  
  372.     NewLayer(&rulerLayer, windowLayer, nil, nil, 0, 0L);
  373.     SetLayerWorld(rulerLayer);
  374.         /* We may not have enough memory to create the offscreen GWorld, so this call may fail,
  375.         ** but that's actually okay.  If NewLayer fails, it returns nil for rulerLayer.  If
  376.         ** we pass nil into SetLayerWorld, it does nothing.  If it does nothing, then the port
  377.         ** is still set to the last port, which is the document's window (SetPort above).
  378.         ** This means that in the worst case, the ruler will flicker when it is redrawn.
  379.         ** We will not blow up. */
  380.  
  381.     EraseRect(&rct);
  382.         /* Erase the offscreen GWorld (or possibly the ruler in the window if low on ram. */
  383.  
  384.     MoveTo(0, 39);        /* Draw a line at the bottom of the ruler that's long enough. */
  385.     Line(20000, 0);
  386.  
  387.     MoveTo(0, 39);                    /* Draw a bottom line for the ruler bottom. */
  388.     Line(20000, 0);                    /* Make it plenty long. */
  389.  
  390.     for (i = 0; i < 16; ++i) {
  391.         MoveTo(72 * i, 0);
  392.         Line(0, 16);                /* Draw inch marks. */
  393.  
  394.         MoveTo(72 * i + 36, 0);        /* Draw 1/2 inch marks. */
  395.         Line(0, 8);
  396.  
  397.         MoveTo(72 * i + 18, 0);        /* Draw 1/4 inch marks. */
  398.         Line(0, 4);
  399.         MoveTo(72 * i + 54, 0);
  400.         Line(0, 4);
  401.     }
  402.  
  403.     InvalLayer(windowLayer, GetEffectiveDstRect(windowLayer), false);
  404.         /* Invalidate the entire layer so it all transfers to the window. */
  405.  
  406.     UpdateLayer(windowLayer);
  407.         /* Transfer all invalid areas. */
  408.  
  409.     ResetLayerWorld(rulerLayer);
  410.         /* Undo the above SetLayerWorld. */
  411.  
  412.     DisposeThisAndBelowLayers(windowLayer);
  413.         /* Dispose of all the layers we created in thif function. */
  414.  
  415.     SetOrigin(oldOrg.h, oldOrg.v);        /* Put origin and port back the way they were. */
  416.     SetPort(oldPort);
  417. }
  418.  
  419.  
  420. The first method seems simpler, but for certain types of rulers, you may wish to use this
  421. technique.  The first method first does a ScrollRect, and then it redraws.  The ScrollRect
  422. erases the area that scrolled into the window.  It then gets redrawn via calling DrawFrame.
  423. This means that for an instant part of the ruler is white.  If the drawing of the ruler is
  424. fast, then this is no big deal.  If however, your ruler is somewhat complicated and takes
  425. a while to draw, then you might want to use this second technique.
  426.  
  427. This second method was also a good way to introduce you to the GWLayers code.  Note that
  428. GWLayers also works for system 6, even if the GWorld calls aren't available.  There is
  429. no system 6 v.s. system 7 compatibility hit if you use GWLayers.
  430.  
  431.  
  432. Another nice feature of the second sample is that DrawFrame is robust.  It doesn't depend
  433. on the framework setting up things nicely (setting the port, setting the origin to 0,0).
  434. It handles these details, which allows you to call it from less polite code, such as the
  435. ScrollFrame procedure for this method.
  436.  
  437. So, DrawFrame is very robust.  How did we really do with the ScrollFrame function?
  438.  
  439. It would seem hard to do anything wrong with the below code:
  440.  
  441. void    ScrollFrame(FileRecHndl frHndl, WindowPtr window, long dh, long dv)
  442. {
  443. #pragma unused (dh, dv)
  444.  
  445.     DrawFrame(frHndl, window);
  446. }
  447.  
  448. Since DrawFrame is smart, ScrollFrame doesn't have to set things up nicely.  So, what's wrong?
  449. The above code isn't very object-oriented.  If your application has only one document type,
  450. then there's nothing wrong with the above code.  However, if you have more than one document
  451. type, then you should code ScrollFrame so that it uses whatever DrawFrame procedure the document
  452. has set.
  453.  
  454. You probably have been wondering how the framework knows what function to call.  In effect, it
  455. doesn't.  A pointer to the appropriate function is stored in the frHndl for each of these
  456. functions.  You can change any of these function pointers.  Let's say that you create a second
  457. document type that has a different ruler.  Possibly the other document type has a vertical
  458. ruler, instead of the horizontal ruler we use in this example.  This second document would have
  459. a different pointer for its DrawFrame function.  However, it could have the same pointer for
  460. the ScrollFrame function.  Given this possibility, here's what should be done in ScrollFrame:
  461.  
  462.  
  463.  
  464. void    ScrollFrame(FileRecHndl frHndl, WindowPtr window, long dh, long dv)
  465. {
  466. #pragma unused (dh, dv)
  467.  
  468.     DrawFrameProcPtr    proc;
  469.  
  470.     if (proc = (*frHndl)->fileState.drawFrameProc)
  471.         (*proc)(frHndl, window);
  472. }
  473.  
  474.  
  475. Unless the DrawFrame procedure pointer is changed, the above code does exactly the same thing.
  476. When a document is created, the application framework sets the procedure pointers to initial
  477. values.  The initial values are the functions found in the “Window2.c” file.  If you have
  478. only one document type, then you can leave these pointers just as they are and simply drop
  479. code into the appropriate functions in “Window2.c”, just as we have been doing so far.
  480. However, if you have multiple document types in your application, you are most likely going
  481. to want to change some of these procedure pointers.
  482.  
  483. With this flexibility comes the responsibility of not making direct calls.  We need to look
  484. up the procedure pointer that the document wants used, and then call that procedure.
  485. An additional feature of the framework is that if the procedure pointer is nil, then that
  486. particular facility isn't used.  This allows you to turn off any of the features of a document
  487. simply by setting the procedure pointer to nil.  So, before you call one of these procedures,
  488. you first get the procedure pointer, and then if it isn't nil, you call it.
  489.  
  490.